package BakaNoPackage;

import java.text.SimpleDateFormat;
import java.util.*;

import BakaNoPackage.Exception.*;
import BakaNoPackage.Donnees.*;

/**
 * 
 * @author Jonas Renaudot
 * @date 05.12.2008
 *
 * Exécute une commande
 */
public class Executeur {

	private String formatDate = "dd.MM.yyyy";
	private AnimeNoBaka animeNoBaka; //AnimeNoBaka sur lequel on exécute les commandes
	
	private Droits droits; //droits d'action sur la bibliothèque
	
	//Détermine le contexte courrant
	private Manga manga = null;
	private Saison saison = null;
	
	/**
	 * Construit la classe Executeur permettant d'executer des commandes sur un AnimeNoBaka
	 * @param animeNoBaka
	 */
	public Executeur(AnimeNoBaka animeNoBaka, Droits droits){
		this.animeNoBaka = animeNoBaka;
		this.droits = droits;
	}
	
	/**
	 * 
	 * @param interpreteur interpréteur à utiliser pour exécuter la commande. Retourne le résultat à afficher.
	 * @throws Exception
	 * @return resultat à afficher dans la console
	 */
	public String execute(Interpreteur interpreteur, ConfirmationStatus confirmation) throws PasLesDroitsException, CommandeNonReconnueException, Exception{
		
		//Détermine la commande
		String commande = interpreteur.getCommande();
		
		if(commande.toLowerCase().equals("new")){ //création d'un élément
			//on a pas les droits
			if(!droits.canEcrire())
				throw new PasLesDroitsException("Vous n'avez pas les droits pour créer un élément");
			
			//appel de la methode privée new
			return this.executeNew(interpreteur);
			
		}else if(commande.toLowerCase().equals("del")){ //supprime
			//on a pas les droits
			if(!droits.canEcrire())
				throw new PasLesDroitsException("Vous n'avez pas les droits pour supprimer un élément");
			
			//appel de la methode privée del
			return this.executeDel(interpreteur, confirmation);
			
		}else if(commande.toLowerCase().equals("set")){ //modifie
			//on a pas les droits
			if(!droits.canEcrire())
				throw new PasLesDroitsException("Vous n'avez pas les droits pour modifier un élément");
			
			//appel de la methode privée set
			return this.executeSet(interpreteur);
			
		}else if(commande.toLowerCase().equals("ls")){ //liste
			if(!droits.canLire())
				throw new PasLesDroitsException("Vous n'avez pas les droits pour lister un élément");
			
			//appel de la methode privée ls
			return this.executeLs(interpreteur);
			
		}else if(commande.toLowerCase().equals("cd")){ //change le contexte
			if(!droits.canLire())
				throw new PasLesDroitsException("Vous n'avez pas les droits pour changer de contexte");
			
			//appel de la methode privée cd
			return this.executeCd(interpreteur);
			
		}else if(commande.toLowerCase().equals("help")){ //Affiche l'aide
			
			//appel de la methode privée help
			return this.executeHelp(interpreteur);
			
		}else{
			return "Commande non reconnue, tappez la commande help pour plus d'informations";
		}
	}
	
	/**
	 * Détermine le prompt en fonction de l'environnement
	 * @return
	 */
	public String getPrompt(){
		
		String prompt = "";
		
		if(manga == null){ //racine
			
			prompt = "AnimeNoBaka";
			
		}else if(saison == null){ //dans un manga
			
			prompt = "AnimeNoBaka/" + manga.getNom();
			
		}else{
			prompt = "AnimeNoBaka/" + manga.getNom() + "/" + saison.getNom();
		}
		
		return prompt + " > ";
	}
	
	/**
	 * Affiche l'aide des commandes
	 * @param interpreteur
	 * @return
	 * @throws CommandeNonReconnueException
	 */
	private String executeHelp(Interpreteur interpreteur) throws CommandeNonReconnueException{
		String aide = "";
		
		aide += "Application AnimeNoBaka - Aide\n";
		aide += "Note sur les textes : les \"\" sont geres et doivent etre utilises si\n";
		aide += "les noms ou les textes contiennent des espaces, de cette maniere : \"Mon texte \\\"ici des guillemets\\\"\"\n";
		aide += "\n";
		aide += "- Liste des mangas / des saisons / des episodes (selon le contexte)\n";
		aide += "    ls\n";
		aide += "- Detail / des saisons / des episodes (selon le contexte)\n";
		aide += "    ls <nom manga / saison / episode>\n";
		aide += "\n";
		aide += "- Ajout\n";
		aide += " - Ajouter un manga / une saison :\n";
		aide += "    new <Nom du manga / de la saison>[ <synopsis> <date de creation yyyy.mm.jj>]\n";
		aide += " - Ajouter un tome (depuis une saison) : \n";
		aide += "    new tome <nom du tome>[ <nombre de page (numerique)>]\n";
		aide += " - Ajouter un episode (depuis une saison) : \n";
		aide += "    new episode <nom du tome>[ <duree en minutes (numerique)]\n";
		aide += "\n";
		aide += "- Modification\n";
		aide += " - Modifier un manga / une saison / une entite :\n";
		aide += "    set <nom (anime, saison ou entite) <parametre> <nouvelle valeure>\n";
		aide += " - Parametres pour les mangas / les saisons :\n";
		aide += "   - nom (texte)\n";
		aide += "   - synopsis (texte)\n";
		aide += "   - date (yyyy.mm.jj)\n";
		aide += "   - commentaire (texte)\n";
		aide += "   - evaluation (numerique)\n";
		aide += " - Parametres pour les tomes :\n";
		aide += "   - nom (texte)\n";
		aide += "   - synopsis (texte)\n";
		aide += "   - pages (numerique)\n";
		aide += " - Parametres pour les  episodes :\n";
		aide += "   - nom (texte)\n";
		aide += "   - synopsis (texte)\n";
		aide += "   - duree (numerique)\n";
		aide += "\n";
		aide += "- Suppression\n";
		aide += " - Modifier un manga / une saison / une entite :\n";
		aide += "    del <nom (anime, saison ou entite)\n";
		aide += "\n";
		aide += "- Changement de contexte\n";
		aide += " - Visiter un manga / une saison :\n";
		aide += "    cd <nom (anime, saison)\n";
		aide += " - Remonter d'un niveau :\n";
		aide += "    cd ..\n";
		return aide;
	}
	
	/**
	 * Ajoute un élément selon le contexte
	 * @param interpreteur ligne à exécuter
	 * @return valeurs à afficher
	 * @throws CommandeNonReconnueException
	 * @throws Exception
	 */
	private String executeNew(Interpreteur interpreteur) throws CommandeNonReconnueException{
		
		//Nom de l'objet à créer
		if(interpreteur.getNombreArguments() == 0)
			throw new CommandeNonReconnueException("Veuillez donner tous les arguments, sous la forme new <nom>");
		
		String nom = interpreteur.getArg(0);
		
		//Test le contexte
		if(manga == null){ //on créé un manga
			
			try{
				Manga manga = animeNoBaka.creerManga(nom);
				
				//Si il y a un synopsis, on l'ajoute
				if(interpreteur.getNombreArguments() > 1){
					manga.setSynopsis(interpreteur.getArg(1));
					
					//si il y a une date, on l'ajoute
					if(interpreteur.getNombreArguments() > 2){
						SimpleDateFormat df = new SimpleDateFormat(formatDate);
						
						try{
							manga.setDateSortie(df.parse(interpreteur.getArg(2)));
						}catch(Exception e){
							return "Le format de date est invalide et n'a donc pas ete pris en compte." +
									" Le manga possedant le nom '" + nom + "' a bien ete cree dans la base de donnees.";
						}
						
						
					}
				}
				
				return "Le manga possedant le nom '" + nom + "' a bien ete cree dans la base de donnees.";
			}catch(MangaExistantException e){
				return "Un manga possedant le nom '" + nom + "' est deja existant, impossible a creer.";
			}
			
		}else if(saison == null){ // on créé une saison
			
			try{
				Saison saison = manga.creerSaison(nom);
				
				//Si il y a un synopsis, on l'ajoute
				if(interpreteur.getNombreArguments() > 1){
					saison.setSynopsis(interpreteur.getArg(1));
					
					//si il y a une date, on l'ajoute
					if(interpreteur.getNombreArguments() > 2){
						SimpleDateFormat df = new SimpleDateFormat(formatDate);
						
						try{
							saison.setDateSortie(df.parse(interpreteur.getArg(2)));
						}catch(Exception e){
							return "Le format de date est invalide et n'a donc pas ete pris en compte." +
									" La saison possedant le nom '" + nom + "' a bien ete cree dans la base de donnees.";
						}
						
						
					}
				}
				
				return "La saison possedant le nom '" + nom + "' a bien ete cree dans la base de donnees.";
			}catch(SaisonExistantException e){
				return "Une saison possedant le nom '" + nom + "' est deja existante, impossible a creer.";
			}
			
		}else{ //on créé un épisode ou un tome
			
			if(interpreteur.getNombreArguments() < 2)
				throw new CommandeNonReconnueException("Mauvaise commande : la creation d'un tome ou d'un manga se fait de la maniere new episode <nom> ou new tome <nom>");
			
			nom = interpreteur.getArg(1);
			
			//TODO: Compléter ici selon entité !!
			if(interpreteur.getArg(0).toLowerCase().equals("episode")){
				
				try{
					Episode episode = saison.creerEpisode(nom);
					
					//Si il y a un nombre de page, on l'ajoute
					if(interpreteur.getNombreArguments() > 2){
						try{
							episode.setDuree(Integer.parseInt(interpreteur.getArg(2)));
						}catch(Exception e){
							return "Le format de duree est invalide et n'a donc pas ete pris en compte." +
							" L'episode possedant le nom '" + nom + "' a bien ete cree dans la base de donnees.";
						}
					}
					
					return "L'episode possedant le nom '" + nom + "' a bien ete cree dans la base de donnees.";
				}catch(EntiteExistantException e){
					return "Un episode possedant le nom '" + nom + "' est deja existante, impossible a creer.";
				}
				
			}else if(interpreteur.getArg(0).toLowerCase().equals("tome")){ //Ajout d'un tome
				try{
					Tome tome = saison.creerTome(nom);
					
					//Si il y a un nombre de page, on l'ajoute
					if(interpreteur.getNombreArguments() > 2){
						try{
							tome.setNbPages(Integer.parseInt(interpreteur.getArg(2)));
						}catch(Exception e){
							return "Le format de nombre de page est invalide et n'a donc pas ete pris en compte." +
							" Le tome possedant le nom '" + nom + "' a bien ete cree dans la base de donnees.";
						}
					}
					
					return "Le tome possedant le nom '" + nom + "' a bien ete cree dans la base de donnees.";
				}catch(EntiteExistantException e){
					return "Un tome possedant le nom '" + nom + "' est deja existante, impossible a creer.";
				}
			}else
				throw new CommandeNonReconnueException("Mauvaise commande : la creation d'un tome ou d'un manga se fait de la maniere new episode <nom> ou new tome <nom>");
		}
	}
	
	/**
	 * Supprime un élément selon le contexte
	 * @param interpreteur ligne à exécuter
	 * @return valeurs à afficher
	 * @throws CommandeNonReconnueException
	 * @throws Exception
	 */
	private String executeDel(Interpreteur interpreteur, ConfirmationStatus confirmation) throws CommandeNonReconnueException, Exception{
		
		//Nom de l'objet à supprimer
		if(interpreteur.getNombreArguments() == 0)
			throw new CommandeNonReconnueException("Veuillez donner tous les arguments, sous la forme del <nom>");
		
		String nom = interpreteur.getArg(0);
		
		//Si il y a besoin d'une confirmation, on le retourne
		if(!confirmation.confirmation){
			confirmation.confirmation = true;
			confirmation.texteConfirmation = "Voulez-vous vraiment effectuer une suppression ? ";
			return null;
		}
		
		//Test le contexte
		if(manga == null){ //on supprime un manga
			
			try{
				Manga manga = animeNoBaka.getManga(nom);
				animeNoBaka.delManga(manga);
			}catch(MangaIntrouvableException e){
				return "Le manga '" + nom + "' est introuvable et ne peut donc pas etre supprime.";
			}
			
			return "Le manga '" + nom + "' a ete correctement supprime.";
			
		}else if(saison == null){ // on supprime une saison
			
			try{
				Saison saison = manga.getSaison(nom);
				manga.delSaison(saison);
			}catch(SaisonIntrouvableException e){
				return "La saison '" + nom + "' est introuvable et ne peut donc pas etre supprime.";
			}
			
			return "La saison '" + nom + "' a ete correctement supprime.";
			
		}else{ //on supprime une entité
			
			//TODO faire ici
			try{
				Entite entite = saison.getEntite(nom);
				saison.delEntite(entite);
			}catch(EntiteIntrouvableException e){
				return "L'entite '" + nom + "' est introuvable et ne peut donc pas etre supprime.";
			}
			
			return "L'entite '" + nom + "' a ete correctement supprime.";
			
		}
	}
	
	/**
	 * Modifie un élément selon le contexte
	 * @param interpreteur ligne à exécuter
	 * @return valeurs à afficher
	 * @throws CommandeNonReconnueException
	 * @throws Exception
	 */
	private String executeSet(Interpreteur interpreteur) throws CommandeNonReconnueException, Exception{
		//Nom de l'objet à modifier
		if(interpreteur.getNombreArguments() < 3)
			throw new CommandeNonReconnueException("Veuillez donner tous les arguments, sous la forme set <nom> <parametre> <valeure>");
		
		String nom = interpreteur.getArg(0);
		String parametre = interpreteur.getArg(1);
		String valeure = interpreteur.getArg(2);
		
		//Test le contexte
		if(manga == null){ //on modifie un manga
			try{
				Manga manga = animeNoBaka.getManga(nom);
				
				//Cherche le parametre a modifier
				
				if(parametre.toLowerCase().equals("nom")){
					
					if(valeure.length() < 1)
						return "Le nom doit avoir une longueure de 1 au minimum";
					
					manga.setNom(valeure);
					
				}else if(parametre.toLowerCase().equals("date")){
					
					SimpleDateFormat df = new SimpleDateFormat(formatDate);
					
					try{
						manga.setDateSortie(df.parse(valeure));
					}catch(Exception e){
						return "Le format de date ( " + valeure + " ) est invalide";
					}
					
				}else if(parametre.toLowerCase().equals("synopsis")){
					manga.setSynopsis(valeure);
				}else if(parametre.toLowerCase().equals("commentaire")){
					manga.setCommentaire(valeure);
				}else if(parametre.toLowerCase().equals("evaluation")){
					
					try{
						manga.setEvaluation(Integer.parseInt(valeure));
					}catch(Exception e){
						return "L'evaluation doit etre une valeure numerique entiere";
					}
					
				}else
					return "Le parametre '" + parametre + "' est invalide. Les parametres valides sont nom <valeure>, date <yyyy.mm.jj>, synopsis <valeure>, commentaire <valeure>, evaluation <nombre entier>.";				
				
				return "La modification a ete correctement effectuee";
				
			}catch(MangaIntrouvableException e){
				return "Le manga '" + nom + "' n'existe pas dans la base de donnees.";
			}
		}else if(saison == null){ // on modifie une saison
			
			try{
				Saison saison = manga.getSaison(nom);
				
				//Cherche le parametre a modifier
				
				if(parametre.toLowerCase().equals("nom")){
					
					if(valeure.length() < 1)
						return "Le nom doit avoir une longueure de 1 au minimum";
					
					saison.setNom(valeure);
					
				}else if(parametre.toLowerCase().equals("date")){
					
					SimpleDateFormat df = new SimpleDateFormat(formatDate);
					
					try{
						saison.setDateSortie(df.parse(valeure));
					}catch(Exception e){
						return "Le format de date est invalide";
					}
					
				}else if(parametre.toLowerCase().equals("synopsis")){
					saison.setSynopsis(valeure);
				}else if(parametre.toLowerCase().equals("commentaire")){
					saison.setCommentaire(valeure);
				}else if(parametre.toLowerCase().equals("evaluation")){
					
					try{
						saison.setEvaluation(Integer.parseInt(valeure));
					}catch(Exception e){
						return "L'evaluation doit etre une valeure numerique entiere";
					}
					
				}else
					return "Le parametre '" + parametre + "' est invalide. Les parametres valides sont nom <valeure>, date <yyyy.mm.jj>, synopsis <valeure>, commentaire <valeure>, evaluation <nombre entier>.";
				
				return "La modification a ete correctement effectuee";
				
			}catch(SaisonIntrouvableException e){
				return "La saison '" + nom + "' n'existe pas dans la base de donnees.";
			}
			
		}else{ //on modifie une entité
			
			try{
				Entite entite = saison.getEntite(nom);
				
				//Cherche le parametre a modifier
				if(parametre.toLowerCase().equals("nom")){
					
					if(valeure.length() < 1)
						return "Le nom doit avoir une longueure de 1 au minimum";
					
					entite.setNom(valeure);
					
				}else if(parametre.toLowerCase().equals("synopsis")){ //change en tome
					
					entite.setSynopsis(valeure);
					
				}else if(parametre.toLowerCase().equals("duree")){ //change en tome
					
					if(entite instanceof Episode){
						try{
							((Episode)entite).setDuree(Integer.parseInt(valeure));
						}catch(Exception e){
							return "La duree doit etre une valeure numerique entiere en minutes";
						}
					}else
						return "Impossible de mettre une duree sur un tome";
					
				}else if(parametre.toLowerCase().equals("pages")){
					if(entite instanceof Tome){
						try{
							((Tome)entite).setNbPages(Integer.parseInt(valeure));
						}catch(Exception e){
							return "Le nombre de page doit etre une valeure numerique entiere en minutes";
						}
					}else
						return "Impossible de mettre une duree sur un tome";
					
				}else
					return "Le parametre '" + parametre + "' est invalide. Les parametres valides sont nom <valeure>, duree <en minutes>(pour un episode), tome <nb pages> (pour un tome).";
				
				return "La modification a ete correctement effectuee";
				
			}catch(EntiteIntrouvableException e){
				return "L'entite '" + nom + "' n'existe pas dans la base de donnees.";
			}
			
		}
	}
	
	/**
	 * Liste des éléments selon le contexte
	 * @param interpreteur ligne à exécuter
	 * @return valeurs à afficher
	 * @throws CommandeNonReconnueException
	 * @throws Exception
	 */
	private String executeLs(Interpreteur interpreteur) throws CommandeNonReconnueException, Exception{
		
		//Nom de l'objet à lister
		/*if(interpreteur.getNombreArguments() == 0)
			throw new CommandeNonReconnueException("Veuillez donner tous les arguments, sous la forme ls <nom>");
		
		String nom = interpreteur.getArg(0);*/
		
		String sortie = "";
		
		//un deuxieme parametre : on affiche les details
		if(interpreteur.getNombreArguments() > 0){
			
			if(manga == null){ //Detail d'un manga
				try{
					Manga manga = animeNoBaka.getManga(interpreteur.getArg(0));
					sortie = manga.toString();
				}catch(MangaIntrouvableException e){
					sortie = "Le manga avec le nom '" + interpreteur.getArg(0) + "' est introuvable.";
				}
				
			}else if(saison == null){ // Detail d'une saison
				try{
					Saison saison = manga.getSaison(interpreteur.getArg(0));
					sortie = saison.toString();
				}catch(SaisonIntrouvableException e){
					sortie = "La saison avec le nom '" + interpreteur.getArg(0) + "' est introuvable.";
				}
			}else{ //on detail d'une entité
				try{
					Entite entite = saison.getEntite(interpreteur.getArg(0));
					sortie = entite.toString();
				}catch(EntiteIntrouvableException e){
					sortie = "L'entite avec le nom '" + interpreteur.getArg(0) + "' est introuvable.";
				}
			}
		}else{ //Affichage de la liste des objets
			
			//Test le contexte
			if(manga == null){ //on liste les mangas
				
				Manga[] mangas = animeNoBaka.getMangas();
				
				if(mangas == null || mangas.length == 0){
					sortie += "La liste de manga est vide";
				}else{
					//Liste tous les mangas
					for(int i = 0; i < mangas.length; i ++){
						sortie += " - " + mangas[i].getNom() + " (" + mangas[i].getDateSortie() + " - " + mangas[i].getEvaluation() + ")\n";
					}
				}
				
			}else if(saison == null){ // on liste les saisons
				
				Saison[] saisons = manga.getSaisons();
				
				//liste toutes les saisons
				if(saisons == null || saisons.length == 0){
					sortie += "La liste des saisons du manga '" + manga.getNom() + "' est vide";
				}else{
					for(int i = 0; i < saisons.length; i ++){
						sortie += " - " + saisons[i].getNom() + " (" + saisons[i].getDateSortie() + " - " + saisons[i].getEvaluation() + ")\n";
					}
				}
			}else{ //on liste les entités
				Entite[] entites = saison.getEntites();
				
				//liste toutes les entites
				if(entites == null || entites.length == 0){
					sortie += "La liste des entites de la saison '" + saison.getNom() + "' est vide";
				}else{
					for(int i = 0; i < entites.length; i ++){
						sortie += " - " + entites[i].getNom() + "\n";
					}
				}
			}
			
		}
		
		
		return sortie;
	}
	
	/**
	 * Modifie le contexte
	 * @param interpreteur ligne à exécuter
	 * @return valeurs à afficher
	 * @throws CommandeNonReconnueException
	 * @throws Exception
	 */
	private String executeCd(Interpreteur interpreteur) throws CommandeNonReconnueException, Exception{
		
		//Nom de l'objet à modifier
		if(interpreteur.getNombreArguments() == 0)
			throw new CommandeNonReconnueException("Veuillez donner tous les arguments, sous la forme set <nom>");
		
		String nom = interpreteur.getArg(0);
		
		//Test le contexte
		if(manga == null){ //on modifie un manga
			
			//si c'est .., on revient en arriere
			if(nom.equals("..")){
				return "impossible de revenir en arriere";
			}
			
			try{
				this.manga = animeNoBaka.getManga(nom);
			}catch(MangaIntrouvableException e){
				return "Le manga '" + nom + "' est introuvable et ne peut donc pas etre visite.";
			}
			
			return "Le contexte pointe sur le manga '" + nom + "'";
			
		}else if(saison == null){ // on modifie une saison

			//si c'est .., on revient en arriere
			if(nom.equals("..")){
				this.manga = null;
				return "Retour a la racine";
			}
			
			try{
				this.saison = manga.getSaison(nom);
			}catch(SaisonIntrouvableException e){
				return "La saison '" + nom + "' est introuvable et ne peut donc pas etre visitee.";
			}
			
			return "Le contexte pointe sur la saison '" + nom + "'";
			
		}else{ //on modifie une entité
			
			//si c'est .., on revient en arriere
			if(nom.equals("..")){
				this.saison = null;
				return "Le contexte pointe sur le manga '" + nom + "'";
			}else
				return "Impossible de faire un CD depuis une saison";
			
		}
	}
}
